using System;
using System.Collections.Generic;
using System.Text;

namespace Noein.GeneticAlgorithm.New
{
    public class GASystem
    {
        /// <summary>
        /// parent count
        /// </summary>
        private int m;

        /// <summary>
        /// offspring count
        /// </summary>
        private int n;

        private List<Individual> parents, offsprings;

        private Selector elitismSeletor;
        private Selector generationSelector;
        private Crossover cross;
        private Mutation mutate;
        private Problem problem;

        private float elitismRate = 0;

        private float generationLife;
        private float generationAge;

        public GASystem(
            int m, int n,
            List<Individual> initialPop,
            Selector elitism, 
            Selector nextGeneration, 
            Crossover cross, 
            Mutation mutate,
            Problem problem,
            float genLife)
        {
            this.m = m;
            this.n = n;
            
            offsprings = initialPop;
            
            elitismSeletor = elitism;
            generationSelector = nextGeneration;
            this.cross = cross;
            this.mutate = mutate;
            this.problem = problem;

            problem.SetSolution(initialPop);
            
            generationLife = genLife;
            generationAge = 0;
        }

        public void Update(float dt)
        {
            generationAge += dt;

            if (generationAge < generationLife)
            {
                // solve problem using current generation solution
                problem.UpdateOutput(dt);
            }
            else
            {
                // measure fitness of problem output
                problem.UpdateFitness();

                // elitism
                int elitismCount;
                if (elitismSeletor == null || 
                    (elitismCount = (int)(m * elitismRate)) == 0 || 
                    parents == null)
                {
                    // no individual survive through more than 1 generation
                    parents = offsprings;
                }
                else
                {
                    // some individuals survive through generation(s)
                    parents = elitismSeletor.Select(parents, elitismCount);
                    parents.AddRange(offsprings);
                }

                // "natural selection" across entire population
                parents = generationSelector.Select(parents, m);

                // birth and evolution by crossover and mutation
                offsprings = cross.Reproduce(parents, n);
                //offsprings = mutate.Reproduce(offsprings, n);
                //offsprings.AddRange(mutate.Reproduce(parents));

                // ready for next simulation cycle
                problem.SetSolution(offsprings);
                generationAge = 0;
            }
        }
    }
}
